#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
@File    :   App_WriteDataById_0x2E_test.py
@Time    :   2022/9/14 13:31:28
@Author  :   YangLiang
@Version :   1.0
@Contact :   yangliang@chengtech.com
@License :   (C)Copyright 2022-2025
@Desc    :   None
'''

# here put the import lib
from gettext import find
from lib2to3.pgen2 import driver
import random
import struct
from pytest_assume.plugin import assume

import pytest
import logging
import time
import requests
from sympy import I
# from sympy import assuming
from commDriver.TS_CAN import *
import allure
import sys
from collections.abc import Iterable

from sqlalchemy import false
sys.dont_write_bytecode = True
import Set_up


class Test_DTCSnapshotRecordNUM_phy(object):

   '''NO Fault'''
   ids = ["Request {0}".format(binascii.b2a_hex(bytearray(dtcNumber), ' ')) for dtcNumber, dtcName in Set_up.DTC_list]
   @allure.severity('normal')
   @allure.story('Test read dtc information 01 service 0x19')
   @allure.description('Test reportDTCByStatusMask of 0x19 in default session with physical addressing')
   @allure.testcase('Test cases address:https://chengtech.pingcode.com/testhub/libraries/62a69d6cb35db4f20e37703a/cases')
   @pytest.mark.parametrize('dtcNumber, dtcName', Set_up.DTC_list, ids=ids)
   def test_DTCSnapshotRecordNUM_01_No_Fault_Default_Phy(self, action,  Driver, dtcNumber,dtcName):
      iStep = 1
      iStep, uds_resp, size = Driver['CAN'].checkReqResp(Driver, '14 FF FF FF', '54', 1, iStep, False, 'UDS', 'noClearFault')
      time.sleep(2)
      if dtcNumber == [0xD3,0x05,0x55] or dtcNumber == [0xD3,0x05,0x56]:
         logging.info('wait 10s')
         time.sleep(10)
      req =  [0x19,0x04] + dtcNumber + [0x01]

      resp = [0x59,0x04] + dtcNumber + [0x00]
      logging.info(f"Test [{dtcName}:{dtcNumber}] by SID_0x19-Subfunction_0x04-DTCExtendedDataRecordNumb_01")
      iStep, uds_resp, size = Driver['CAN'].checkReqResp(Driver, req, resp, 0, iStep, False, 'UDS', 'noClearFault')
      

   ids = ["Request {0}".format(binascii.b2a_hex(bytearray(dtcNumber), ' ')) for dtcNumber, dtcName in Set_up.DTC_list]
   @allure.severity('normal')
   @allure.story('Test read dtc information 01 service 0x19')
   @allure.description('Test reportDTCByStatusMask of 0x19 in extended session with physical addressing')
   @allure.testcase('Test cases address:https://chengtech.pingcode.com/testhub/libraries/62a69d6cb35db4f20e37703a/cases')
   @pytest.mark.parametrize('dtcNumber, dtcName', Set_up.DTC_list, ids=ids)
   def test_DTCSnapshotRecordNUM_01_No_Fault_extended_Phy(self, action,  Driver, dtcNumber,dtcName):
      iStep = 1
      iStep, uds_resp, size = Driver['CAN'].checkReqResp(Driver, '14 FF FF FF', '54', 3, iStep, False, 'UDS', 'noClearFault')
      time.sleep(2)
      if dtcNumber == [0xD3,0x05,0x55] or dtcNumber == [0xD3,0x05,0x56]:
         logging.info('wait 10s')
         time.sleep(10)
      req =  [0x19,0x04] + dtcNumber + [0x01]
      resp = [0x59,0x04] + dtcNumber + [0x00]
      logging.info(f"Test [{dtcName}:{dtcNumber}]  by SID_0x19-Subfunction_0x04-DTCExtendedDataRecordNumb_01")
      iStep,uds_resp, size = Driver['CAN'].checkReqResp(Driver, req, resp, 3, iStep, False, 'UDS', 'noClearFault')

   
   ids = ["Request {0}".format(binascii.b2a_hex(bytearray(dtcNumber), ' ')) for dtcNumber, dtcName in Set_up.DTC_list]
   @allure.severity('normal')
   @allure.story('Test read dtc information FF service 0x19')
   @allure.description('Test reportDTCByStatusMask of 0x19 in default session with physical addressing')
   @allure.testcase('Test cases address:https://chengtech.pingcode.com/testhub/libraries/62a69d6cb35db4f20e37703a/cases')
   @pytest.mark.parametrize('dtcNumber, dtcName', Set_up.DTC_list, ids=ids)
   def test_DTCSnapshotRecordNUM_FF_No_Fault_Default_Phy(self, action,  Driver, dtcNumber,dtcName):
      iStep = 1
      iStep, uds_resp, size = Driver['CAN'].checkReqResp(Driver, '14 FF FF FF', '54', 1, iStep, False, 'UDS', 'noClearFault')
      time.sleep(2)
      if dtcNumber == [0xD3,0x05,0x55] or dtcNumber == [0xD3,0x05,0x56]:
         logging.info('wait 10s')
         time.sleep(10)
      req =  [0x19,0x04] + dtcNumber + [0xFF]

      resp = [0x59,0x04] + dtcNumber + [0x00]
      logging.info(f"Test [{dtcName}:{dtcNumber}] by SID_0x19-Subfunction_0x04-DTCExtendedDataRecordNumb_01")
      iStep,uds_resp, size = Driver['CAN'].checkReqResp(Driver, req, resp, 0, iStep, False, 'UDS', 'noClearFault')

   ids = ["Request {0}".format(binascii.b2a_hex(bytearray(dtcNumber), ' ')) for dtcNumber, dtcName  in Set_up.DTC_list]
   @allure.severity('normal')
   @allure.story('Test read dtc information FF service 0x19')
   @allure.description('Test reportDTCByStatusMask of 0x19 in extended session with physical addressing')
   @allure.testcase('Test cases address:https://chengtech.pingcode.com/testhub/libraries/62a69d6cb35db4f20e37703a/cases')
   @pytest.mark.parametrize('dtcNumber, dtcName', Set_up.DTC_list, ids=ids)
   def test_DTCSnapshotRecordNUM_FF_No_Fault_extended_Phy(self, action,  Driver, dtcNumber,dtcName):
      iStep = 1
      iStep, uds_resp, size = Driver['CAN'].checkReqResp(Driver, '14 FF FF FF', '54', 3, iStep, False, 'UDS', 'noClearFault')
      time.sleep(2)
      if dtcNumber == [0xD3,0x05,0x55] or dtcNumber == [0xD3,0x05,0x56]:
         logging.info('wait 10s')
         time.sleep(10)
      req =  [0x19,0x04] + dtcNumber + [0xFF]

      resp = [0x59,0x04] + dtcNumber + [0x00]
      logging.info(f"Test [{dtcName}:{dtcNumber}]  by SID_0x19-Subfunction_0x04-DTCExtendedDataRecordNumb_01")
      iStep,uds_resp, size = Driver['CAN'].checkReqResp(Driver, req, resp, 3, iStep, False, 'UDS', 'noClearFault')


   @allure.severity('normal')
   @allure.title('Test abnormal voltage DTCSnapshot Record By DTCNumber in default session')
   @allure.description('Test abnormal voltage DTCSnapshot Record By DTCNumber in default session with physical addressing')
   @allure.testcase('https://chengtech.pingcode.com/testhub/libraries/630357052177ea2394680877/cases')
   def test_DTCSnapshotRecordByDTCNumber_01_Default_Phy(self, action,  Driver):
      iStep = 1
      iStep,uds_resp,size = Driver['CAN'].checkReqResp(Driver, '19 04 D0 97 87 01', '59 04 D0 97 87 00', 1, iStep, False, 'UDS', 'ClearFault')
      self.SnapShot02(Driver, iStep)
      

      
   @allure.severity('normal')
   @allure.title('Test abnormal voltage DTCSnapshot Record By DTCNumber in default session')
   @allure.description('Test abnormal voltage DTCSnapshot Record By DTCNumber in extended session with physical addressing')
   @allure.testcase('https://chengtech.pingcode.com/testhub/libraries/630357052177ea2394680877/cases')
   def test_DTCSnapshotRecordByDTCNumber_01_Extended_Phy(self, action,  Driver):
      iStep = 1
      iStep,uds_resp,size = Driver['CAN'].checkReqResp(Driver, '19 04 F0 03 16 01', '59 04 F0 03 16 00', 3, iStep, False, 'UDS', 'ClearFault')
      self.SnapShot01(Driver, iStep)


   @allure.severity('normal')
   @allure.title('Test DTC Snapshot Record data in default session')
   @allure.description('Test DTC Snapshot Record data in in default session with physical addressing')
   @allure.testcase('https://chengtech.pingcode.com/testhub/libraries/630357052177ea2394680877/cases')
   def test_DTCSnapshotRecordByDTCNumber_FF_Default_Phy(self, action,  Driver):
      iStep = 1
      iStep,uds_resp,size = Driver['CAN'].checkReqResp(Driver, '19 04 D0 97 87 01', '59 04 D0 97 87 00', 1, iStep, False, 'UDS', 'ClearFault')
      self.SnapShot02(Driver, iStep)

   @allure.severity('normal')
   @allure.title('Test DTC Snapshot Record data in extended session')
   @allure.description('Test DTC Snapshot Record data in in extended session with physical addressing')
   @allure.testcase('https://chengtech.pingcode.com/testhub/libraries/630357052177ea2394680877/cases')
   def test_DTCSnapshotRecordByDTCNumber_FF_Extended_Phy(self, action,  Driver):
      iStep = 1
      iStep,uds_resp,size = Driver['CAN'].checkReqResp(Driver, '19 04 F0 03 16 01', '59 04 F0 03 16 00', 3, iStep, False, 'UDS', 'ClearFault')
      self.SnapShot01(Driver, iStep)




   def SnapShot01(self, Driver, iStep):
      for i in range(2):
         setVolt = random.randint(8, 15)
         setYear = random.randint(2025, 2050)
         setMonth = random.randint(1, 12)
         setDay = random.randint(1, 28)   
         setHour = random.randint(0, 23)
         setMinute = random.randint(0, 59)
         setSecond = random.randint(0, 59)
         setVspeed = random.randint(0, 120)
         iStep = self.setSnapshotData(Driver, iStep, setVolt, setYear, setMonth, setDay, setHour, setMinute, setSecond, 0x02, 0x0000, setVspeed)
         # 设置电压为8v,生成低压故障
         iStep = Driver['CAN'].SetVolt(Driver, 8, 2, iStep)
         # 恢复电压
         iStep = Driver['CAN'].SetVolt(Driver, 12, 2, iStep)
         # 设置车速2kph
         iStep = Driver['CAN'].SetVehicleSpeed(Driver, iStep, 2)
         time.sleep(1)
         iStep,uds_resp,size = Driver['CAN'].checkReqResp(Driver, '22 D0 00', '62 D0 00', 0, iStep, False)
         iStep,uds_resp,size = Driver['CAN'].checkReqResp(Driver, '19 04 F0 03 16 01','59 04 F0 03 16 2E 01 09', 0, iStep, False, 'UDS', 'noClearFault')
         
         iStep = self.validate_snapshot_data(uds_resp, iStep, setVolt, setYear, setMonth, setDay, setHour, setMinute, setSecond, 0x02, 0x0000, setVspeed)

         iStep,uds_resp,size = Driver['CAN'].checkReqResp(Driver, '14 FF FF FF', '54', 0, iStep, False)

      return iStep

 
   def SnapShot02(self, Driver, iStep):
      
      setVolt = random.randint(8, 15)
      setYear = random.randint(2025, 2050)
      setMonth = random.randint(1, 12)
      setDay = random.randint(1, 28)   
      setHour = random.randint(0, 23)
      setMinute = random.randint(0, 59)
      setSecond = random.randint(0, 59)
      setVspeed = random.randint(0, 120)
      iStep = self.setSnapshotData(Driver, iStep, setVolt, setYear, setMonth, setDay, setHour, setMinute, setSecond, 0x02, 0x0000, setVspeed)

      # 第一次生成故障
      logging.info("Generate the first fault")
      # 生成故障 D0 97 87
      iStep = Driver['CAN'].SetLostCommunicationStatus(Driver, iStep, False)
      time.sleep(2)
      iStep = Driver['CAN'].SetLostCommunicationStatus(Driver, iStep, True)
      # 设置车速 = 0kph
      Driver['CAN'].SetVehicleSpeed(Driver, iStep,  0)
      time.sleep(1)
      iStep,uds_resp,size = Driver['CAN'].checkReqResp(Driver, '19 04 D0 97 87 01', '59 04 D0 97 87 2E 01 09', 0, iStep, False, 'UDS', 'noClearFault')

      iStep = self.validate_snapshot_data(uds_resp, iStep, setVolt, setYear, setMonth, setDay, setHour, setMinute, setSecond, 0x02, 0x0000, setVspeed)

      iStep,uds_resp,size = Driver['CAN'].checkReqResp(Driver, '14 FF FF FF', '54', 0, iStep, False)

      return iStep
      




   def setSnapshotData(self, Driver, iStep, voltage, year, month, day, hour, minute, second, power_mode, odometer, vehicleSpeed):
      """
      设置快照数据，包括车速、电压、日期时间等。
      :param Driver: 驱动对象，用于与 CAN 总线进行通信
      :param iStep: 当前步骤编号
      :param voltage: 电压值
      :param year: 年份
      :param month: 月份
      :param day: 日期
      :param hour: 小时
      :param minute: 分钟
      :param second: 秒钟
      :param power_mode: 电源模式
      :param odometer: 里程数
      :param vehicleSpeed: 车速
      :return: 更新后的步骤编号
      """
      with allure.step(f"Step{iStep}: Set snapshot data"):
         # 设置车速
         Driver['CAN'].SetVehicleSpeed(Driver, iStep,  vehicleSpeed)
         # 设置电压
         value = voltage * 1000
         byte1 = (value >> 7) & 0xFF  # 取高8位 → 0x75 (117)
         byte2 = (value & 0x7F) << 1  # 取低7位并左移1位 → 0x30 (48)
         BattURaw = [byte1, byte2]
         Driver['CAN'].update_signal_data(0x243, index=[2,3],valueBytes=BattURaw)
         # 设置年月日 2025-3-22 14:35:55
         Driver['CAN'].update_signal_data(0x107, [0x00, year-2000, month, day, hour, minute, second << 2, 0x25])
         logging.info(f"Set snapshot data: voltage={voltage}V, time={year}-{month}-{day} {hour}:{minute}:{second}, power_mode={power_mode}, odometer={odometer}, vehicleSpeed={vehicleSpeed}km/h")
         logging.info(f"wait 2s")
         time.sleep(2)
         iStep += 1
         return iStep
   
   def validate_snapshot_data(self, uds_resp, iStep, voltage, year, month, day, hour, minute, second, power_mode, odometer, vehicleSpeed):
      """
      验证 UDS 响应中的快照数据。
      :param uds_resp: UDS 响应数据
      :param iStep: 当前步骤编号
      :param voltage: 期望的电压值
      :param year: 期望的年份
      :param month: 期望的月份
      :param day: 期望的日期
      :param hour: 期望的小时
      :param minute: 期望的分钟
      :param second: 期望的秒钟
      :param power_mode: 期望的电源模式
      :param odometer: 期望的里程数
      :param vehicleSpeed: 期望的车速
      :return: 更新后的步骤编号
      """
      with allure.step(f"Step{iStep}: Determine whether the snapshot voltage data is {voltage}v"):
         # 验证DID标识符
         with assume: assert uds_resp[8:10] == [0xD0,0x00], logging.error("电压DID标识错误")
         # 解析电压值 (D000规范: 2字节，factor=0.025, offset=5)
         raw_voltage = (uds_resp[10] << 8) | uds_resp[11]
         converted_voltage = raw_voltage * 0.025 + 5
         # 验证电压范围
         with assume: assert abs(converted_voltage -voltage) < 1, logging.error(f"电压值{converted_voltage:.2f}V超出{voltage}V允许范围")
         iStep += 1

      with allure.step(f"Step{iStep}: Verify timestamp data"):
         # 验证时间DID标识符
         with assume: assert uds_resp[12:14] == [0xD0,0x01], logging.error("时间DID标识错误")
         #  解析BCD时间格式 (D001规范) 验证具体时间
         with assume: assert year   == 2000 + uds_resp[14], logging.error(f"年份错误, 期望{year},实际是{2000 + uds_resp[14]}")
         with assume: assert month  == uds_resp[15],        logging.error(f"月份错误，期望的是{month},实际是{uds_resp[15]}")
         with assume: assert day    == uds_resp[16],        logging.error(f"日期错误，期望的是{day},实际是{uds_resp[16]}")
         with assume: assert hour   == uds_resp[17],        logging.error(f"小时错误，期望的是{hour},实际是{uds_resp[17]}")
         with assume: assert minute == uds_resp[18],        logging.error(f"分钟错误, 期望的是{minute},实际是{uds_resp[18]}")
         with assume: assert second == uds_resp[19],        logging.error(f"秒钟错误, 期望的是{second},实际是{uds_resp[19]}")
         iStep += 1

      with allure.step(f"Step{iStep}: Verify power mode"):
         # 验证电源模式DID标识符
         with assume: assert uds_resp[20:22] == [0xD0,0x02], logging.error("电源模式DID标识错误")
         # 解析电源模式 (D002规范)
         with assume: assert power_mode == uds_resp[22], logging.error("电源模式状态错误")
         iStep += 1

      with allure.step(f"Step{iStep}: Verify odometer"):
         # 验证里程DID标识符
         with assume: assert uds_resp[23:25] == [0xD0,0x03], logging.error("里程DID标识错误")
         # 解析里程值 (D003规范: 2字节)
         with assume: assert odometer == (uds_resp[25] << 8) | uds_resp[26], logging.error("里程值异常")
         iStep += 1

      with allure.step(f"Step{iStep}: Verify vehicle speed"):
         # 验证车速DID标识符
         with assume: assert uds_resp[28:30] == [0xD0,0x04], logging.error("车速DID标识错误")
         # D004: 2字节车速（factor=0.05625）
         vehicle_speed = (uds_resp[30] << 8 | uds_resp[31]) * 0.05625
         with assume: assert abs(vehicle_speed - vehicleSpeed) < 1, logging.error("车速值异常")
         iStep += 1

      with allure.step(f"Step{iStep}: Verify Battery SOC"):
         # 验证Battery SOC DID标识符
         with assume: assert uds_resp[32:34] == [0xD0,0x05], logging.error("Battery SOC DID标识错误")
         iStep += 1
      with allure.step(f"Step{iStep}: Verify ActGearLvr"):
         # 验证Battery SOC DID标识符
         with assume: assert uds_resp[36:38] == [0xD0,0x06], logging.error("ActGearLvr DID标识错误")
         iStep += 1
      with allure.step(f"Step{iStep}: Verify ActGearLvr"):
         # 验证Battery SOC DID标识符
         with assume: assert uds_resp[39:41] == [0xD0,0x07], logging.error("PT Ready DID标识错误")
         iStep += 1
      with allure.step(f"Step{iStep}: Verify ActGearLvr"):
         # 验证Battery SOC DID标识符
         with assume: assert uds_resp[42:44] == [0xD0,0x08], logging.error("Car Mode DID标识错误")
         iStep += 1
      return iStep